Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Introduction

This is the reference documentation for the nice_things framework. All public functions in every module and preprocessor macro are documented here.

You can find a list of all classes, modules, and macros on the left side of the screen, sorted by category.

You can press / (slash) on the keyboard or click the magnifying glass icon in the header at the top of the screen to search.

If you want to learn the basics of how to use the framework, check out the README file in the source-code repository.

Most modules contain a single public function or read-only variable with the same name as the module file, but a few modules may publish several functions/variables. To make it easier to find a function/variable by name, bellow is an index of all public names sorted alphabetically.

Index of public names

Error status codes

  • 1: Expected failure.
  • 3: <package> not found.
  • 11: Invalid or missing arguments (abort).
  • 12: Invalid reference (abort).
  • 12: Invalid self reference <&self> (abort).
  • 13: Failed to create directory (on initialization) (abort).
  • 13: Failed to get user id (on initialization) (abort).
  • 13: Unexpected error (on initialization) (abort).
  • 14: Base directory missing.
  • 15: Failed to create directory.
  • 16: Failed to write file (when option -f is used).
  • 17: File exists (when option -t is used).
  • 18: IFS is null (abort).
  • 19: Invalid operand; list items cannot be lists.
  • 20: Could not find file at <file>.
  • 21: Invalid property value in config file. Expected string but found array.
  • 22: <file> is not a symbolic link.
  • 23: Any <pathname> failed to resolve.
  • 24: No FD available, resource exhausted.
  • 25: File exists (when <mode> is w).
  • 26: Invalid variable name.
  • 122: Some command failed (if fails to retrieve correct status code).
  • 123: Unexpected error (abort).
  • 123: Unexpected error.

nice_things/async/Future.sh

The Future class implements this popular synchronization primitive for asynchronous programming in pure POSIX sh. The API is inspired by libdex.

Futures—also called Promises in some programming languages—are eager. The async command starts executing from the moment the Future constructor is invoked.

Usage examples

dl_main=#{{{ new Future }}} -n 'main.host' curl https://main.host…
dl_mirror=#{{{ new Future }}} -n 'mirror.host' curl https://mirror.host…
download=#{{{ new Future }}} -n 'download' -o await_any -p "$dl_main" "$dl_mirror"
timeout=#{{{ new Future }}} -n 'timeout 60s' timeout 60

# Either timeout or get result of main/mirror request
if await_first winner "$download" "$timeout"; then
	# Futures do not propagate automatically, we must traverse manually to the inner Future
	await "$winner" "" dl_winner
	Future_get_name "$dl_winner" url
	log_debug "Finished downloading from '${url}'"
else
	Future_get_name "$winner" name
	log_error "Failed to download resource with '${name}' failure"
fi

# Destroy objects to free resources
for future in "$dl_main" "$dl_mirror" "$download" "$timeout"; do
	Future_destructor "$future"
done

await

Since 0.3.0 · Source

import "{ await }" from nice_things/async/Future_await.sh

Synopsis
await <&self> [<out_file>] [<out_var>]

Configuration

Description
Await the Future to settle. The returned status code is the status of the async command.

When the Future constructor is invoked with the -o option to capture the standard output of the async command, await offers two choices of output variables to get it:

  • <out_file> to get the path to the file containing the output.
  • <out_var> to get the contents of the output assigned to a variable.

Warning

Note that reading the file when assigning to <out_var> is done with the read builtin command in pure sh. No external utilities, like cat, are used. This means the assignment should be fast when the output is known to be small, but can get exponentially slower for larger outputs.

For this reason, the <out_var> parameter has limited usefulness to cases when the output of the command is known to be small. For all other cases, the <out_file> parameter should be preferred. Then, you can assign the contents of the file to a variable with the cat utility, if that is what you need.

Despite the unconventional shorter name, without the Future_ prefix, this implementation of await is an instance method of Future.

Options

Operands

  • <&self>: Self reference.
  • <out_file>: Output variable; the path to the output file will be written to this variable (if -o was specified in the Future constructor). Optional, or can be set to the null string to ignore when specifying <out_var>.
  • <out_var>: Output variable; the async command's output will be written to this variable (if -o was specified in the Future constructor). Optional.

Stdin

Stdout

Stderr

Exit status

  • 0: The async command completed successfully.
  • >0: The async command failed.
  • 12: Invalid self reference <&self> (abort).
  • 123: Unexpected error.

Abort
Aborts if self reference <&self> is invalid.

Usage examples

await "$future" output_file

await_all

Since 0.3.0 · Source

import "{ await_all }" from nice_things/async/Future_await_all.sh

Synopsis
await_all <&future>…

Configuration

Description
Await all <&future>s to settle. Return status code 0 if all succeeded, otherwise return the status code of the last failure.

Note that await_all always waits until all <&future>s have settled, even if there are early failures. If you want to return early on the first failure, consider the await_all_race function instead.

await_all is not a method, but a static function in the Future class. It can operate on many objects.

Options

Operands
<&future>: Reference to a Future object.

Stdin

Stdout

Stderr

Exit status

  • 0: All Futures completed successfully.
  • >0: Some Future failed.
  • 12: Invalid reference (abort).

Abort
Aborts if reference <&future> is invalid.

Usage examples

if await_all "$future1" "$future2" "$future3"; then
	log_debug "All futures succeeded!"
else
	log_warn "Some future failed!"
fi

await_all_race

Since 0.3.0 · Source

import "{ await_all_race }" from nice_things/async/Future_await_all_race.sh

Synopsis
await_all_race <&future>…

Configuration

Description
Await all <&future>s to settle. Return status code 0 if all succeeded, otherwise return the status code of the first failure.

Compared to await_all, which always runs through until all <&future>s have settled, await_all_race returns early on the first failure.

await_all_race is not a method, but a static function in the Future class. It can operate on many objects.

Options

Operands
<&future>: Reference to a Future object.

Stdin

Stdout

Stderr

Exit status

  • 0: All Futures completed successfully.
  • >0: Some Future failed.
  • 12: Invalid reference (abort).

Abort
Aborts if reference <&future> is invalid.

Usage examples

if await_all_race "$future1" "$future2" "$future3"; then
	log_debug "All futures succeeded!"
else
	log_warn "Some future failed!"
fi

await_any

Since 0.3.0 · Source

import "{ await_any }" from nice_things/async/Future_await_any.sh

Synopsis
await_any {-p | <out_var>} <&future>…

Configuration

Description
Await any <&future> to complete successfully. Return status code 0 if any succeeded, otherwise return status code 1 if all failed.

await_any races the <&future>s and returns as soon as one succeeds. A reference to the winning <&future> will be assigned to <out_var>, unless the -p option is specified, in which case the reference will be printed to stdout. It is mandatory to specify one of these two arguments, and they cannot be used together.

await_any is not a method, but a static function in the Future class. It can operate on many objects.

Options
-p: Print output instead of assigning to <out_var>.

Operands

  • <out_var>: Output variable; the result will be written to this variable. Skip when the -p option is specified. Or you can set this to the null string if you don't need a reference to the winning Future.
  • <&future>: Reference to a Future object.

Stdin

Stdout
If the -p option is specified, the result is printed to stdout. Otherwise, stdout is not used.

Stderr

Exit status

  • 0: Some Future completed successfully.
  • >0: All Futures failed.
  • 12: Invalid reference (abort).

Abort
Aborts if reference <&future> is invalid.

Usage examples

if await_any winner "$future1" "$future2" "$future3"; then
	log_debug "The winner is '${winner}'"
else
	log_warn "All futures failed!"
fi

await_first

Since 0.3.0 · Source

import "{ await_first }" from nice_things/async/Future_await_first.sh

Synopsis
await_first {-p | <out_var>} <&future>…

Configuration

Description
Await any <&future> to settle, no matter if it succeeded or failed. Return status code of the <&future> that settled first.

await_first races the <&future>s and returns as soon as one settles. A reference to the winning <&future> will be assigned to <out_var>, unless the -p option is specified, in which case the reference will be printed to stdout. It is mandatory to specify one of these two arguments, and they cannot be used together.

await_first is not a method, but a static function in the Future class. It can operate on many objects.

Options
-p: Print output instead of assigning to <out_var>.

Operands

  • <out_var>: Output variable; the result will be written to this variable. Skip when the -p option is specified. Or you can set this to the null string if you don't need a reference to the winning Future.
  • <&future>: Reference to a Future object.

Stdin

Stdout
If the -p option is specified, the result is printed to stdout. Otherwise, stdout is not used.

Stderr

Exit status

  • 0: The first Future to settle succeeded.
  • >0: The first Future to settle failed.
  • 12: Invalid reference (abort).

Abort
Aborts if reference <&future> is invalid.

Usage examples

if await_first winner "$future1" "$future2" "$future3"; then
	log_debug "The winner is '${winner}' and it succeeded"
else
	log_debug "The winner is '${winner}' and it failed"
fi

Future

Since 0.3.0 · Source

import "{ Future }" from nice_things/async/Future.sh

Synopsis
Future <&self> [-n <name>] [-o] [--] <command> [<arg>…]

Configuration

Description
Constructor for the Future class.

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

  • -n <name>: Give a name to this object as a way of identifying it.
  • -o: Save the command's standard output to a file.
  • --: End of options.

Operands

  • <command>: The command to execute asynchronously.
  • <arg>: Optional argument to <command>.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).
  • 12: Invalid self reference <&self> (abort).
  • 123: Unexpected error (abort).

Abort

  • Aborts on invalid or missing arguments.
  • Aborts if self reference <&self> is invalid.
  • Aborts on unexpected error.

Usage examples

timer=#{{{ new Future }}} -n 'A 10 seconds timer' sleep 10

Future_destructor

Since 0.3.0 · Source

import "{ Future_destructor }" from nice_things/async/Future_destructor.sh

Synopsis
Future_destructor <&self>

Configuration

Description
Clear all data associated with the object.

If the Future has not settled yet, it will be cancelled, by sending a KILL signal to its process.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Future_destructor "$future"

Future_get_name

Since 0.3.0 · Source

import "{ Future_get_name }" from nice_things/collections/Future_get_name.sh

Synopsis
Future_get_name <&self> <out_var>

Configuration

Description
Get name of the Future. A Future object only has a name if that was specified in the Future constructor using the -n <name> option.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Future_get_name "$future" name

Future_is_settled

Since 0.3.0 · Source

import "{ Future_is_settled }" from nice_things/collections/Future_is_settled.sh

Synopsis
Future_is_settled <&self>

Configuration

Description
Test synchronously if the Future is already settled at the time of this method's invocation. Return status code 0 if true, 1 if false.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: The Future is already settled.
  • 1: The Future is not settled yet.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

if Future_is_settled "$future"; then
	log_debug "The future is already settled"
fi

nice_things/async/Future_racer.sh

Future_racer is a pseudo-class used internally by some functions in the Future class to race Futures against each other. It is the backbone of asynchronous functions like await_all_race, await_any and await_first.

The functions in this module are lower-level than the functions in the Future class. Unless you need the extra async flexibility of Future_racer, you should prefer the higher-level functions of the Future class instead.

The Future_racer API is a pseudo-class, with a constructor and destructor functions, but unlike a real class, only a single instance can exist at a time. The pseudo-object is an iterator, with only a next method. What is special about this iterator is that it picks Futures from the list asynchronously, in the order they settle.

Usage examples

Future_racer_new "$future1" "$future2" "$future3"
while Future_racer_next future status; do
	: # Check "$status" and/or do something with "$future"
done
Future_racer_destructor

Future_racer_destructor

Since 0.3.0 · Source

import "{ Future_racer_destructor }" from nice_things/async/Future_racer.sh

Synopsis
Future_racer_destructor

Configuration

Description
Clear all data associated with the racer pseudo-object.

Options

Operands

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

Future_racer_destructor

Future_racer_new

Since 0.3.0 · Source

import "{ Future_racer_new }" from nice_things/async/Future_racer.sh

Synopsis
Future_racer_new <&future>…

Configuration

Description
Create a new racer pseudo-object.

Options

Operands
<&future>: Reference to a Future object.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid reference (abort).

Abort
Aborts if reference <&future> is invalid.

Usage examples

Future_racer_new "$future1" "$future2" "$future3"

Future_racer_next

Since 0.3.0 · Source

import "{ Future_racer_next }" from nice_things/async/Future_racer.sh

Synopsis
Future_racer_next [<out_var>] [<out_status>]

Configuration

Description
Await the next Future to settle. A reference to the Future is assigned to <out_var>, and its status code is assigned to <out_status>.

Options

Operands

  • <out_var>: Output variable; a reference to the Future will be written to this variable.
  • <out_status>: Output variable; the Future's status code will be written to this variable.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Next Future retrieved.
  • 1: Iteration finished.

Abort

Usage examples

Future_racer_next future status

nice_things/cli/OptionsParser.sh

The OptionsParser class implements a parser for CLI options compatible with the POSIX standard and with added support for GNU extensions, like long options and option-argument separated by an equals sign.

One or more short options without option-arguments, followed by at most one short option that takes an option-argument, can be grouped together behind a single - (hyphen-minus) character.

Option-arguments cannot be optional. If an option takes an argument, it is mandatory.

Option-arguments must come in a separate argument, or be separated by an = equals sign. This implementation does not support the historical behavior of accepting an option-argument as part of the same string as the option, without a separator.

Processing of -- (the end of options flag) is done automatically.

Usage examples

# Implement parse function
parse_option() {
	case "$2" in
	-a) option_a=1 ;;
	-l | --long-option) option_l=1 ;;
	-o | --option-with-arg) OptionsParser_has_opt_arg "$1" 1 && option_o=$3 ;;
	-Z) option_Z=1 ;;
	*) abort "Unknown option '${2}'" 2 ;;
	esac
}

# Initialize variables
option_a='' option_l='' option_o='' option_Z=''

# Parse options from positional parameters
options_parser=#{{{ new OptionsParser }}} parse_option "$@"

# Shift options out of positional parameters
OptionsParser_get_count "$options_parser" opt_count
shift "$opt_count"

# Destroy parser object
OptionsParser_destructor "$options_parser"

OptionsParser

Since 0.3.0 · Source

import "{ OptionsParser }" from nice_things/cli/OptionsParser.sh

Synopsis
OptionsParser <&self> <parser_function> [<args>…]

Configuration

Description
Constructor for the OptionsParser class. The name of a user-defined parsing function must be passed in as the <parser_function> parameter, followed by the full arguments list that contains the options to parse—usually that will be "$@".

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

Operands

  • <&self>: Self reference.
  • <parser_function>: Name of the function that will handle the options.
  • <args>: The arguments list containing the options to parse.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

options_parser=#{{{ new OptionsParser }}} parse_option "$@"

OptionsParser_destructor

Since 0.3.0 · Source

import "{ OptionsParser_destructor }" from nice_things/cli/OptionsParser_destructor.sh

Synopsis
OptionsParser_destructor <&self>

Configuration

Description
Clear all data associated with the object.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

OptionsParser_destructor "$options_parser"

OptionsParser_get_count

Since 0.3.0 · Source

import "{ OptionsParser_get_count }" from nice_things/cli/OptionsParser_get_count.sh

Synopsis
OptionsParser_get_count <&self> <out_var>

Configuration

Description
Get number of arguments processed as options and option-arguments.

This information is useful for shifting the options out of the positional parameters, for processing operands.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

OptionsParser_get_count "$options_parser" opt_count
shift "$opt_count"

OptionsParser_has_opt_arg

Since 0.3.0 · Source

import "{ OptionsParser_has_opt_arg }" from nice_things/cli/OptionsParser_has_opt_arg.sh

Synopsis
OptionsParser_has_opt_arg <&self> [<abort_on_failure>]

Configuration

Description
This method should be used only inside the function used as the OptionsParser constructor's <parser_function>, to indicate that the current option being processed requires an option-argument. If the option-argument is missing from the options being processed, status code 1 will be returned.

If <abort_on_failure> is set, this function will abort the process instead of returning on failure, and a message will be printed to log_error informing the user about the missing option-argument.

Options

Operands

  • <&self>: Self reference.
  • <abort_on_failure>: Set to 1 to abort on failure.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 1: Missing option-argument.
  • 12: Invalid self reference <&self> (abort).

Abort

  • Aborts on missing option-argument if <abort_on_failure> is specified.
  • Aborts if self reference <&self> is invalid.

Usage examples

parse_option() {
	case "$2" in
	-a) OptionsParser_has_opt_arg "$1" 1 && option_a=$3 ;;
	-b)
		{ OptionsParser_has_opt_arg "$1" && [ -n "${3-}" ] && option_b=$3; } ||
			abort "Invalid option argument ${2} '${3-}'" 2
		;;
	*) abort "Unknown option '${2}'" 2 ;;
	esac
}

nice_things/collections/Array.sh

The Array class implements an array type in pure POSIX sh. It exists because List—the only way to represent structured data in sh—is lossy. The classes provided by the framework in the collections directory aim to bring robust data structure types to POSIX sh, and Array is one of those types.

The API is influenced by JavaScript Arrays. Method signatures are as close to that API as is possible to do in sh. One exception is indexing, which starts from 1, as is common in sh, and go up to length inclusive. Methods that take an index argument also accept negative numbers to search from the end of the Array.

Being a class, while working with Array, you will be passing references around. The internal state of the object is managed by the framework's object system, you only have to keep a reference to it. The constructor returns a reference, and every method is this class take that reference as the first argument. The user is responsible for calling the destructor method to free the resources used by the object.

There are many ways to iterate over the Array. You can loop over the indexes from 1 to length inclusive, calling the get method on each index. Or you can consume the Array in a loop with the pop or shift methods.

Usage examples

# Create an Array: letters=[a,b]
letters=#{{{ new Array }}} a b

# Add elements to the Array: letters=[a,b,c,d]
Array_push "$letters" c d

# Get the length of the Array
Array_length "$letters" length

# Iterate over all elements of the Array
index=1
while [ "$index" -le "$length" ]; do
	Array_get "$letters" element "$index"
	# Do something with "$element"
	# ...
	index=$((index + 1))
done

# Destroy the Array
Array_destructor "$letters"

Array

Since 0.3.0 · Source

import "{ Array }" from nice_things/collections/Array.sh

Synopsis
Array <&self> [<element>…]

Configuration

Description
Constructor for the Array class. Can optionally take initial state as arguments.

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <element>: Element to push to the new Array.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e

Array_destructor

Since 0.3.0 · Source

import "{ Array_destructor }" from nice_things/collections/Array_destructor.sh

Synopsis
Array_destructor <&self>

Configuration

Description
Clear all data associated with the object.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Array_destructor "$array"

Array_get

Since 0.3.0 · Source

import "{ Array_get }" from nice_things/collections/Array_get.sh

Synopsis
Array_get <&self> <out_var> <index>

Configuration

Description
Get an element from the Array by index.

<index> starts from 1 and go to the length of the Array inclusive.

Negative <index> can be used to count from the end of the Array, where -1 retrieves the last element, and -length the first.

The error code 1 will be returned if <index> is out-of-bounds.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.
  • <index>: Index of the element to retrieve starting from 1.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: <index> out-of-bounds.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_get "$letters" letter -2
log_debug "$letter" # d

Array_includes

Since 0.3.0 · Source

import "{ Array_includes }" from nice_things/collections/Array_includes.sh

Synopsis
Array_includes <&self> <element>

Configuration

Description
Test if <element> is included in the Array. Return status code 0 if true, 1 if false.

Options

Operands

  • <&self>: Self reference.
  • <element>: The value to search.

Stdin

Stdout

Stderr

Exit status

  • 0: <element> is included in the Array.
  • 1: <element> is not included in the Array.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
if Array_includes "$letters" d; then
	log_debug "'d' is included"
fi

Array_index_of

Since 0.3.0 · Source

import "{ Array_index_of }" from nice_things/collections/Array_index_of.sh

Synopsis
Array_index_of <&self> <out_var> <element>

Configuration

Description
Get the index of the first occurrence of <element> in the Array. Return status code 0 if <element> is found in the Array, 1 if not found.

Note

This operation is slower than most other methods in this class.
If you don't need the index and are only testing for an element's existence, use the faster Array_includes instead.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.
  • <element>: The value to search.

Stdin

Stdout

Stderr

Exit status

  • 0: <element> found in the Array.
  • 1: <element> not found in the Array.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
if Array_index_of "$letters" index d; then
	log_debug "'d' is at index ${index}" # 'd' is at index 4
fi

Array_length

Since 0.3.0 · Source

import "{ Array_length }" from nice_things/collections/Array_length.sh

Synopsis
Array_length <&self> <out_var>

Configuration

Description
Get the length of the Array.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_length "$letters" len
log_debug "$len" # 5

Array_pop

Since 0.3.0 · Source

import "{ Array_pop }" from nice_things/collections/Array_pop.sh

Synopsis
Array_pop <&self> [<out_var>]

Configuration

Description
Remove the last element from the Array. The removed element is assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: No element removed; the Array is empty.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_pop "$letters" letter
log_debug "$letter" # e

Array_push

Since 0.3.0 · Source

import "{ Array_push }" from nice_things/collections/Array_push.sh

Synopsis
Array_push <&self> <element>…

Configuration

Description
Add elements to the end of the Array.

Options

Operands

  • <&self>: Self reference.
  • <element>: An element to be added to the Array.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c
Array_push "$letters" d e f # a b c d e f

Array_shift

Since 0.3.0 · Source

import "{ Array_shift }" from nice_things/collections/Array_shift.sh

Synopsis
Array_shift <&self> [<out_var>]

Configuration

Description
Remove the first element from the Array. The removed element is assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: No element removed; the Array is empty.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_shift "$letters" letter
log_debug "$letter" # a

Array_slice

Since 0.3.0 · Source

import "{ Array_slice }" from nice_things/collections/Array_slice.sh

Synopsis
Array_slice <&self> <out_var>

Configuration

Description
Clone the Array. The new Array is assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Array_slice "$array" slice

Array_splice

Since 0.3.0 · Source

import "{ Array_splice }" from nice_things/collections/Array_splice.sh

Synopsis
Array_splice <&self> <index> <delete_count> [<element>…]

Configuration

Description
Mutate the Array by deleting and/or adding elements at the specific <index>.

<index> starts from 1 and can go up beyond the length of the Array. Adding elements beyond the end of the Array with this method do not make it sparse. When index is greater than the length of the Array, the first new element will be inserted at length + 1.

Negative <index> can be used to count from the end of the Array, where -1 is the last element, and -length the first.

<delete_count> is mandatory and should be set to 0 if you do not want to delete any elements.

Options

Operands

  • <&self>: Self reference.
  • <index>: Index at which to start changing the array.
  • <delete_count>: The number of elements to delete.
  • <element>: Value to add to the Array.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_splice "$letters" 3 2 C D # a b C D e

Array_to_quoted

Since 0.3.0 · Source

import "{ Array_to_quoted }" from nice_things/collections/Array_to_quoted.sh

Synopsis
Array_to_quoted <&self> <out_var>

Configuration

Description
Get a quoted textual representation of the Array that is appropriate to use as an argument to the eval builtin command.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} a b c d e
Array_to_quoted "$letters" quoted_letters # "'a' 'b' 'c' 'd' 'e'"

Array_unshift

Since 0.3.0 · Source

import "{ Array_unshift }" from nice_things/collections/Array_unshift.sh

Synopsis
Array_unshift <&self> <element>…

Configuration

Description
Add elements to the beginning of the Array.

Options

Operands

  • <&self>: Self reference.
  • <element>: An element to be added to the Array.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

letters=#{{{ new Array }}} d e f
Array_unshift "$letters" a b c # a b c d e f

nice_things/collections/List.sh

This page documents several modules related to Lists.

The List pseudo-class is not a real class. These modules are named as if they were members of a class just for organization and easier maintenance.

Data is stored in a simple variable using the shell's standard way of representing lists: a text string using the value of the IFS variable as the internal field separator. This means Lists are unsafe to expand and lossy. They cannot contain every possible string value (notably, they cannot contain other Lists.) For that reason, Lists should only be used in strict_mode, which alleviates the issues by setting IFS to a single, rarely-used control character, and makes Lists safe to expand by disabling pathname expansion (AKA globbing).

Note

If you need a lossless way to save a list, consider using an Array object instead. Arrays can also represent matrices, since they can contain objects, including other Arrays. But, being objects, they are harder to work with and have higher performance cost.

As is common in sh, indexing starts from 1, and go up to length inclusive. Functions that take an index argument also accept negative numbers to search from the end of the List.

List functions are generic over their arguments; they use standard positional parameters. This means not only Lists can be passed as argument, but any values in any way an arguments list can be created. For that reason, Lists must be expanded unquoted when passed as argument to these functions, to expand one argument for each item in the List. That is why push and concat functions are not needed. You can add items to a list or concatenate many lists in the same way it is done for strings, by just expanding them near each other:

# Create a list: letters=[a,b]
list letters a b
# Add items to the list: letters=[a,b,c,d]
list letters ${letters} c d
# Create another list: more_letters=[e,f]
list more_letters e f
# Concatenate 2 lists: all_letters=[a,b,c,d,e,f]
list all_letters ${letters} ${more_letters}

list

Since 0.3.0 · Source

import "{ list }" from nice_things/collections/List.sh

Synopsis
list <out_var> <item>…

Configuration

Description
Turn arguments into a list of items separated by IFS. When IFS contains more than one character, the first character of IFS is used as field-separator.

Lists are single-level. It is not possible to create a list of lists.

Lists are meant to be used in strict_mode. Without strict_mode this list format is unreliable due to accidental pathname expansions and IFS-splitting.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <item>: An item to be added to the List.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 18: IFS is null (abort).
  • 19: Invalid operand; list items cannot be lists.

Abort
Aborts if IFS is null.

Usage examples

list letters a b c d e
for letter in $letters; do
	printf 'The letter is: %s\n' "$letter"
done

contains

Since 0.3.0 · Source

import "{ contains }" from nice_things/collections/List_contains.sh

Synopsis
contains <item> <list>…

Configuration

Description
Test if <item> is contained in the List. Return status code 0 if true, 1 if false.

Options

Operands

  • <item>: The value to search.
  • <list>: An unquoted List.

Stdin

Stdout

Stderr

Exit status

  • 0: <item> is contained in the List.
  • 1: <item> is not contained in the List.

Abort

Usage examples

if contains d a b c d e; then
	printf '"d" is contained\n'
fi

fmt

Since 0.3.0 · Source

import "{ fmt }" from nice_things/collections/List_fmt.sh

Synopsis
fmt <out_var> <pattern> <list>…

Configuration

Description
Use a printf-style pattern to format each argument, return a List separated by IFS.

Compared to just using printf, this function does not print the <pattern> when the List is empty.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <pattern>: A printf-style format pattern.
  • <list>: An unquoted List.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 18: IFS is null (abort).

Abort
Aborts if IFS is null.

Usage examples

fmt objects './build/%s.o' main mod
printf '%s\n' $objects # ./build/main.o ./build/mod.o

list_from

Since 0.3.0 · Source

import "{ list_from }" from nice_things/collections/List_from.sh

Synopsis
list_from <out_var> <string> [<separator>]

Configuration

Description
Turn <string> into a List splitting at each occurrence of separator. If <separator> is not provided the default value of IFS is used (space, tab, line-feed).

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <string>: The text string to split.
  • <separator>: One or more characters to be used as field-separator.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 18: IFS is null (abort).
  • 19: Invalid operand; list items cannot be lists.

Abort
Aborts if IFS is null.

Usage examples

csv="a,b,c,d,e"
list_from fields "$csv" ","

get

Since 0.3.0 · Source

import "{ get }" from nice_things/collections/List_get.sh

Synopsis
get <out_var> <index> <list>…

Configuration

Description
Get an item from a List by index.

<index> starts from 1 and go to the length of the List inclusive.

Negative <index> can be used to count from the end of the List, where -1 retrieves the last item, and -length the first.

This function does not check if <index> is within bounds. The behavior when trying to get an index that does not exist in the List is dependent on if strict_mode is in use, in which case the set -u (nounset) configuration should cause it to abort with a shell-dependent error code.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <index>: Index of the item to retrieve starting from 1.
  • <list>: An unquoted List.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.

Abort
Some shells may abort in strict_mode if <index> is out-of-bounds.

Usage examples

get letter 3 a b c d e
printf '%s\n' "$letter" # c

is_list

Since 0.3.0 · Source

import "{ is_list }" from nice_things/collections/List_is_list.sh

Synopsis
is_list <arg>…

Configuration

Description
Test if any of the arguments is itself a List according to the current value of IFS.

Options

Operands
<arg>: Any string.

Stdin

Stdout

Stderr

Exit status

  • 0: One or more of the arguments is a List.
  • 1: None of the arguments is a List.

Abort

Usage examples

if is_list "$@"; then
	printf 'Some argument contains an IFS character\n'
fi

length

Since 0.3.0 · Source

import "{ length }" from nice_things/collections/List_length.sh

Synopsis
length <out_var> <list>…

Configuration

Description
Get the length of a List.

The <list> operand should be expanded unquoted as this function returns the length of the positional parameters list.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <list>: An unquoted List.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

length len a b c d e
printf '%s\n' "$len" # 5

to_string

Since 0.3.0 · Source

import "{ to_string }" from nice_things/collections/List_to_string.sh

Synopsis
to_string <list>…

Configuration

Description
Print a human-readable string representation of a List.

The output of this function is for debug purposes only. This is not a reversible serialization of the list, and the format is not standardized and can change.

Options

Operands
<list>: An unquoted List.

Stdin

Stdout
A human-readable representation of a List.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

log_debug "Positional parameters: $(to_string "$@")"

readonly:list_is_terminated

Since 0.3.0 · Source

import "{ readonly:list_is_terminated }" from nice_things/collections/List_is_terminated.sh

Synopsis
[ -n "$list_is_terminated" ]

Configuration

Description
Will be set to 1 if List should have a terminating field separator in the current shell. Will be null otherwise.

This readonly variable is used internally by the list functions to decide if a trailing field separator should be printed. It was created specifically for zsh, which differs from all other shells by not accepting a terminating field separator. This behavior makes List slightly less robust in zsh, since it cannot differentiate an empty List from a List containing a single null value.

Options

Operands

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful definition.
  • 13: Unexpected error (on initialization) (abort).

Abort
Aborts if initialization fails.

Usage examples

if [ -n "$list_is_terminated" ]; then
	printf '%s' "${IFS%"${IFS#?}"}"
fi

nice_things/collections/Map.sh

The Map class implements a Hash Map in pure POSIX sh. This data structure is optimized for random access over iteration. The order of insertion of keys is not preserved. The API is influenced by JavaScript Maps.

Being a class, while working with Map, you will be passing references around. The internal state of the object is managed by the framework's object system, you only have to keep a reference to it. The constructor returns a reference, and every method is this class take that reference as the first argument. The user is responsible for calling the destructor method to free the resources used by the object.

Usage examples

# Create a Map: letters={a=1,b=2}
letters=#{{{ new Map }}} a 1 b 2

# Add mappings to the Map: letters={a=1,b=2,c=3,d=4}
Map_set "$letters" c 3 d 4

# Get the values of mappings "b" and "c": value_b=2 value_c=3
Map_get "$letters" value_b b value_c c

# Destroy the Map
Map_destructor "$letters"

Map

Since 0.3.0 · Source

import "{ Map }" from nice_things/collections/Map.sh

Synopsis
Map <&self> [<key> <value>]…

Configuration

Description
Constructor for the Map class. Can optionally take initial mappings as arguments in the form of <key> <value> pairs.

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <key>: Key for a new mapping.
  • <value>: Value of new mapping.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three 4 four 5 five

Map_clear

Since 0.3.0 · Source

import "{ Map_clear }" from nice_things/collections/Map_clear.sh

Synopsis
Map_clear <&self>

Configuration

Description
Delete all mappings and set size to 0.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Map_clear "$map"

Map_clone

Since 0.3.0 · Source

import "{ Map_clone }" from nice_things/collections/Map_clone.sh

Synopsis
Map_clone <&self> <out_var>

Configuration

Description
Clone the Map. The new Map is assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Map_clone "$map" clone

Map_delete

Since 0.3.0 · Source

import "{ Map_delete }" from nice_things/collections/Map_delete.sh

Synopsis
Map_delete <&self> <out_var> <key>

Configuration

Description
Delete the mapping identified by <key>. The deleted value is assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the deleted value will be written to this variable. You can set this to the null string if you don't need the deleted value.
  • <key>: Key of mapping to delete.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: <key> does not exist in the Map.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three 4 four 5 five
Map_delete "$numbers" deleted 3
log_debug "Deleted value: '${deleted}'" # three

Map_destructor

Since 0.3.0 · Source

import "{ Map_destructor }" from nice_things/collections/Map_destructor.sh

Synopsis
Map_destructor <&self>

Configuration

Description
Clear all data associated with the object.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Map_destructor "$map"

Map_get

Since 0.3.0 · Source

import "{ Map_get }" from nice_things/collections/Map_get.sh

Synopsis
Map_get <&self> [<out_var> <key>]…

Configuration

Description
Get values from the Map by key.

Multiple values can be retrieved with a single invocation. If any <key> does not exist in the Map, status code 1 will be returned and the variable named by its <out_var> argument will be unset.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the value will be written to this variable.
  • <key>: Key of the value to retrieve.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: One or more <key> does not exist in the Map.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three 4 four 5 five
if Map_get "$numbers" value 3; then
	log_debug "Got value: '${value}'" # three
else
	log_debug "Mapping '3' does not exist"
fi

Map_has

Since 0.3.0 · Source

import "{ Map_has }" from nice_things/collections/Map_has.sh

Synopsis
Map_has <&self> <key>

Configuration

Description
Test if <key> exists in the Map. Return status code 0 if true, 1 if false.

Options

Operands

  • <&self>: Self reference.
  • <key>: The key to search.

Stdin

Stdout

Stderr

Exit status

  • 0: <key> exists in the Map.
  • 1: <key> does not exist in the Map.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three 4 four 5 five
if Map_has "$numbers" 3; then
	log_debug "'3' exists"
fi

Map_set

Since 0.3.0 · Source

import "{ Map_set }" from nice_things/collections/Map_set.sh

Synopsis
Map_set <&self> [<key> <value>]…

Configuration

Description
Add mappings to the Map. Several mappings can be added with a single invocation.

Options

Operands

  • <&self>: Self reference.
  • <key>: The key to add.
  • <value>: The value to add.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three
Map_set "$numbers" 4 four 5 five

Map_size

Since 0.3.0 · Source

import "{ Map_size }" from nice_things/collections/Map_size.sh

Synopsis
Map_size <&self> <out_var>

Configuration

Description
Get the size of the Map.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

numbers=#{{{ new Map }}} 1 one 2 two 3 three 4 four 5 five
Map_size "$numbers" size
log_debug "Numbers size: '${size}'" # 5

Map_to_quoted

Since 0.3.0 · Source

import "{ Map_to_quoted }" from nice_things/collections/Map_to_quoted.sh

Synopsis
Map_to_quoted <&self> <out_var>

Configuration

Description
Get a quoted textual representation of the Map that is appropriate to use as an argument to the eval builtin command.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Map_to_quoted "$map" quoted_map

nice_things/config/Config.sh

The Config class can be used to parse an INI-based config format and retrieve the values of properties from it.

Usage examples

package_conf=#{{{ new Config }}} ./nice_package.conf || exit
if Config_get_string "$package_conf" pkg_version '[]' version; then
	log_debug "The package version is '${pkg_version}'"
else
	log_debug "Config property 'version' not found"
fi

Config

Since 0.3.0 · Source

import "{ Config }" from nice_things/config/Config.sh

Synopsis
Config <&self> <file>

Configuration

Description
Constructor for the Config class. Parse the config from the file specified in the <file> parameter and initialize the object with the values of all properties in it.

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <file>: A config file to parse.

Stdin

Stdout

Stderr
log_error, log_warn.

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).
  • 20: Could not find file at <file>.

Abort
Aborts if self reference <&self> is invalid.

Usage examples

package_conf=#{{{ new Config }}} ./nice_package.conf

Config_destructor

Since 0.3.0 · Source

import "{ Config_destructor }" from nice_things/config/Config_destructor.sh

Synopsis
Config_destructor <&self>

Configuration

Description
Clear all data associated with the object.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

Config_destructor "$package_conf"

Config_get_array

Since 0.3.0 · Source

import "{ Config_get_array }" from nice_things/config/Config_get_array.sh

Synopsis
Config_get_array <&self> <out_var> <section> <property>

Configuration

Description
Get value of a property of type Array. A reference to the Array will be assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.
  • <section>: Name of the section, including the square brackets.
  • <property>: Name of the property.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: <property> not found.
  • 11: Invalid or missing arguments (abort).
  • 12: Invalid self reference <&self> (abort).

Abort

  • Aborts on invalid or missing arguments.
  • Aborts if self reference <&self> is invalid.

Usage examples

Config_get_array "$package_conf" pkg_authors '[]' author

Config_get_string

Since 0.3.0 · Source

import "{ Config_get_string }" from nice_things/config/Config_get_string.sh

Synopsis
Config_get_string <&self> <out_var> <section> <property>

Configuration

Description
Get value of a property of type string. The result will be assigned to <out_var>.

Options

Operands

  • <&self>: Self reference.
  • <out_var>: Output variable; the result will be written to this variable.
  • <section>: Name of the section, including the square brackets.
  • <property>: Name of the property.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 1: <property> not found.
  • 11: Invalid or missing arguments (abort).
  • 12: Invalid self reference <&self> (abort).
  • 21: Invalid property value in config file. Expected string but found array.

Abort

  • Aborts on invalid or missing arguments.
  • Aborts if self reference <&self> is invalid.

Usage examples

Config_get_string "$package_conf" pkg_version '[]' version

nice_things/io/FileDescriptor.sh

FileDescriptor is a "singleton" pseudo-class. There is only one instance of global state.

File descriptors are an extremely scarce resource in POSIX sh. There are only 10 of them, and 3 are already in use by stdin, stdout and stderr, leaving only 7 available for program use. If a shared library uses an FD by number, it is prone to collisions with other libraries using FDs. The open and close functions solve this issue by picking FDs from a list and keeping an internal global state of FDs in use. As long as libraries and application code use these functions to allocate and release FDs, no collisions will happen. These functions make 7 FDs available, starting from number 9 down to number 3.

Usage examples

output_file=/path/to/file.out

# Open FD for writing, with complete error handling
open output_fd "$output_file" w || {
	case $? in
	24) abort "Failed to open file at '${output_file}': No FD available, resource exhausted." ;;
	25) abort "Failed to open file at '${output_file}' for writing: File exists." ;;
	*) abort "Unknown error while trying to open file at '${output_file}'" ;;
	esac
}

# Write to FD
printf 'Some data\n' >&"$output_fd"

# Close FD
close "$output_fd"

close

Since 0.3.0 · Source

import "{ close }" from nice_things/io/FileDescriptor_close.sh

Synopsis
close <fd>

Configuration

Description
Close the file descriptor with number <fd>.

Options

Operands
<fd>: The number of a file description retrieved with the open function.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).
  • 123: Unexpected error (abort).

Abort

  • Aborts on invalid or missing arguments.
  • Aborts on unexpected error.

Usage examples

close "$file_fd"

open

Since 0.3.0 · Source

import "{ open }" from nice_things/io/FileDescriptor_open.sh

Synopsis
open <out_var> <file> [<mode>]

Configuration

Description
Open a file descriptor and assign its number to <out_var>.

If <file> is a number between 0 and 9, the same file opened in the FD identified by that number will be opened.

By default, <file> is opened read-only. An optional <mode> parameter can be used to select a different mode:

  • w: Write – uses the > redirection operator.
  • fw: Force-write – uses the >| redirection operator.
  • rw: Read-write – uses the <> redirection operator.

Options

Operands

  • <out_var>: Output variable; the FD number will be written to this variable.
  • <file>: The file to open; if a number between 0-9, will open the same file as that FD.
  • <mode>: Optional mode, read-only by default; May be w (write), fw (force-write), rw (read-write).

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).
  • 24: No FD available, resource exhausted.
  • 25: File exists (when <mode> is w).
  • 123: Unexpected error (abort).

Abort

  • Aborts on invalid or missing arguments.
  • Aborts on unexpected error.

Usage examples

open file_fd "$file"

nice_things/lang/PipeStatus.sh

The PipeStatus class allows to detect when errors occur at any stage of a pipeline. It's meant to provide functionality similar to setting the pipefail shell option in bash, but compatible with any POSIX shell.

Since commands in a pipeline run in a subshell, this class uses a runtime file as a side-channel to record the status code returned by the commands. The resulting status of the class is the status code of the first command to fail, or 0 if all commands succeed.

An object of this class can be used only once. The catch method is a destructor, after it has been invoked, the object cannot be used anymore.

Usage examples

#{{{
import "{ PipeStatus }" from nice_things/lang/PipeStatus.sh
import "{ catch }" from nice_things/lang/PipeStatus_catch.sh
import "{ try }" from nice_things/lang/PipeStatus_try.sh
#}}}
ps=#{{{ new PipeStatus }}}
try "$ps" command : | try "$ps" command :
catch "$ps" || {
	log_error "Pipeline failed with status $?"
}

PipeStatus

Since 0.3.0 · Source

import "{ PipeStatus }" from nice_things/lang/PipeStatus.sh

Synopsis
PipeStatus <&self>

Configuration

Description
Constructor for the PipeStatus class.

Usually, this constructor function should be invoked with the new macro, which takes care of creating the <&self> reference to the newly created object.

Options

Operands

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 12: Invalid self reference <&self> (abort).
  • 123: Unexpected error (abort).

Abort

  • Aborts if self reference <&self> is invalid.
  • Aborts on unexpected error.

Usage examples

ps=#{{{ new PipeStatus }}}

catch

Since 0.3.0 · Source

import "{ catch }" from nice_things/lang/PipeStatus_catch.sh

Synopsis
catch <&self>

Configuration

Description
Check if any command has failed. Returns the status code of the first command to fail, or 0 if all commands succeeded.

Options

Operands
<&self>: Self reference.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • >0: Some command failed.
  • 12: Invalid self reference <&self> (abort).
  • 122: Some command failed (if fails to retrieve correct status code).

Abort
Aborts if self reference <&self> is invalid.

Usage examples

catch "$ps" || log_error "Pipeline failed with status $?"

try

Since 0.3.0 · Source

import "{ try }" from nice_things/lang/PipeStatus_try.sh

Synopsis
try <&self> <command> [<arg>…]

Configuration

Description
Execute <command> and save its returned status code.

Options

Operands

  • <&self>: Self reference.
  • <command>: The command to execute.
  • <arg>: Optional argument to <command>.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort
Always exits the current process.

Usage examples

try "$ps" command : | try "$ps" command :

nice_things/async/timeout.sh

timeout

Since 0.3.0 · Source

import "{ timeout }" from nice_things/async/timeout.sh

Synopsis
timeout <time>

Configuration

Description
Sleep for <time> seconds, then return with status code 1.

Internally, this function calls the platform's sleep utility. POSIX defines that sleep only accepts an integer number of seconds, so use that for maximum portability. Many implementations also support decimal fractions. Some implementations may support other units beyond seconds by appending a character or word to the <time> argument, or as an extra parameter, but those extensions are not portable.

The timeout function was created to support use-cases of the Future class, to enable racing a timeout against other asynchronous operations, returning an error status code if the timer runs out before the operation completes.

Options

Operands
<time>: The time in seconds to wait.

Stdin

Stdout

Stderr

Exit status
1: Timeout elapsed.

Abort

Usage examples

timeout 60

nice_things/auth/start_user_id.sh

readonly:start_user_id

Since 0.3.0 · Source

import "{ readonly:start_user_id }" from nice_things/auth/start_user_id.sh

Synopsis
"${start_user_id}"

Configuration

Description
The id of the user who started the program.

Options

Operands

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful definition.
  • 13: Failed to get user id (on initialization) (abort).

Abort
Aborts if initialization fails.

Usage examples

log_debug "Program started by UID: ${start_user_id}"

nice_things/fs/basename.sh

basename

Since 0.3.0 · Source

import "{ basename }" from nice_things/fs/basename.sh

Synopsis
basename <pathname> [<suffix>]

Configuration

Description
Print <pathname> with any leading directory components removed. If specified, also remove a trailing <suffix>.

This is a pure sh implementation of the utility specified in POSIX.

Options

Operands

  • <pathname>: A path name.
  • <suffix>: An optional suffix.

Stdin

Stdout
The resulting filename is printed to stdout, trailed by a line-feed character.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

# Get name of current program
cmd=$(basename "$0")

# Print file name with extension removed
basename include/stdio.h .h # stdio

nice_things/fs/dirname.sh

dirname

Since 0.3.0 · Source

import "{ dirname }" from nice_things/fs/dirname.sh

Synopsis
dirname <pathname>

Configuration

Description
Print the directory portion of <pathname>, with the last component removed.

This is a pure sh implementation of the utility specified in POSIX.

Options

Operands

  • <pathname>: A path name.

Stdin

Stdout
The resulting pathname is printed to stdout, trailed by a line-feed character.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

dirname /usr/bin/ # /usr

nice_things/fs/glob.sh

glob

Since 0.3.0 · Source

import "{ glob }" from nice_things/fs/glob.sh

Synopsis
glob <pattern>…

Configuration

Description
Perform pathname expansion (AKA globbing) on arguments. Prints a List of matched files.

Compared to pathname expansion as performed by the shell, this function behaves differently in that it does not print out the input pattern if that pattern fails to match any files.

This function includes a pure sh implementation of globstar, similar to that feature in bash. It works on any shell. You can use the globstar operator ** (double-asterisks) in a path segment to expand any number of subdirectories recursively. Only a single globstar in <pattern> will be treated as such. In case <pattern> contains multiple globstar operators, the later ones will be treated as if they were the single asterisk operator.

Warning

This function prints a List and can only be safely used in strict_mode.

Options

Operands
<pattern>: A glob pattern.

Stdin

Stdout
A List of matched files. Each <pattern> can expand to zero or more filenames.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

#{{{ strict_mode }}}
# Get all sh files in current and child directories
shell_scripts=$(glob ./**/*.sh)

nice_things/fs/readlink.sh

Since 0.3.0 · Source

import "{ readlink }" from nice_things/fs/readlink.sh

Synopsis
readlink <file>

Configuration

Description
Print value of a symbolic link.

Note

This is not a pure sh implementation because it depends on the external ls utility to read the target of the symlink. This works consistently because the output of ls is strictly specified in POSIX, but it means this function can be slower than most other functions in the framework.

Options

Operands
<file>: Path to a symbolic link.

Stdin

Stdout
The result is printed to stdout, trailed by a line-feed character.

Stderr

Exit status

  • 0: Successful completion.
  • 22: <file> is not a symbolic link.
  • 123: Unexpected error (abort).

Abort
Aborts on unexpected error.

Usage examples

# Read the value of a link
link_target=$(readlink /path/to/link)

nice_things/fs/realpath.sh

realpath

Since 0.3.0 · Source

import "{ realpath }" from nice_things/fs/realpath.sh

Synopsis
realpath <pathname>…

Configuration

Description
Print the resolved absolute path name; all but the last component must exist. This implementation tries to closely emulate the default behavior of the utilities with the same name in Busybox and GNU coreutils.

When many operands are passed and one fails to resolve, an error message is printed to log_error, and operation continues until the last operand is processed.

Note

This is not a pure sh implementation because it depends on the external ls utility to read the target of symlinks. This works consistently because the output of ls is strictly specified in POSIX, but it means this function can be slower than most other functions in the framework. The slow branch is only reached when the last component of <pathname> is a symlink.

Options

Operands
<pathname>: A path name.

Stdin

Stdout
A new-line separated list of absolute paths, one for each operand.

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 23: Any <pathname> failed to resolve.

Abort

Usage examples

# Get the real path to the current program
program=$(realpath "$0")

nice_things/io/cat.sh

cat

Since 0.3.0 · Source

import "{ cat }" from nice_things/io/cat.sh

Synopsis
cat [-u] [--] [<file>…]

Configuration

Description
Concatenate <file>(s) to stdout. With no <file>, or when <file> is -, read stdin.

This function is a simple POSIX-compatible cat utility in pure shell script.

Note

This function only exists to provide a pure sh implementation. cat is a very basic core utility and is always likely to be available and will be much faster than this function. You should prefer the platform's cat command over this function in most cases.

Options

  • -u: Ignored.
  • --: End of options.

Operands
<file>: Optional file to read, or - to read stdin.

Stdin
Stdin will be read when no <file> parameter is specified, or when <file> is -.

Stdout
Will echo all files' contents to stdout.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

cat /path/to/file

nice_things/io/echo.sh

echo

Since 0.3.0 · Source

import "{ echo }" from nice_things/io/echo.sh

Synopsis
echo [<message>…]

Configuration

Description
Portable echo that takes no options for consistent behavior across shells.

Just like the echo shell builtin command, this function prints the <message> arguments, separated by a space character when many arguments are used, and terminated with a line-feed character.

This function exists because POSIX echo is poorly specified and has inconsistent behavior across shells. You should prefer the printf builtin command for consistent behavior, but if you must use echo, this function makes it consistent.

Options

Operands
<message>: An optional message to print.

Stdin

Stdout
The message is printed to stdout.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

echo "Some text"

nice_things/io/read_line.sh

read_line

Since 0.3.0 · Source

import "{ read_line }" from nice_things/io/read_line.sh

Synopsis
read_line <out_line> [<out_trailing_lf>]

Configuration

Description
Read a line from stdin.

This function is meant to simplify the common pattern of reading a line without any processing and keeping the trailing line-feed character when present and without losing data when a file does not end in a new line, like so:

trailing_lf="
"
while IFS= read -r line || { trailing_lf= && [ -n "$line" ]; }; do
	: # Do something with "${line}${trailing_lf}"
done

As can be seen in the example above, reading a file correctly in sh, without losing any data, is awfully difficult. The same example using the read_line function is much more sensible:

while read_line line trailing_lf; do
	: # Do something with "${line}${trailing_lf}"
done

When the trailing line-feed is not necessary, the second parameter, <out_trailing_lf>, can be omitted.

Options

Operands

  • <out_line>: Output variable; the line will be written to this variable.
  • <out_trailing_lf>: Output variable; optional; the trailing line-feed character, if it exists, will be written to this variable.

Stdin
The line will be read from stdin.

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: End of file.

Abort

Usage examples

# Echo all text from stdin to stdout
while read_line line trailing_lf; do
	printf '%s' "${line}${trailing_lf}"
done

nice_things/lang/assign.sh

assign

Since 0.3.0 · Source

import "{ assign }" from nice_things/lang/assign.sh

Synopsis
assign <out_var> <command> [<arg>…]

Configuration

Description
Assign the output of a command <command> to a variable <out_var>, and prevent truncation of trailing line-feeds that usually result from command substitution.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <command>: The command to execute.
  • <arg>: Optional argument to <command>.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • >0: <command> failed.

Abort

Usage examples

# Assign the complete text of a file to a variable without loss of data
assign file_data cat /path/to/file

nice_things/lang/assign_variable.sh

assign_variable

Since 0.3.0 · Source

import "{ assign_variable }" from nice_things/lang/assign_variable.sh

Synopsis
assign_variable <variable_assignment>

Configuration

Description
Use indirection to dynamically assign a variable. The argument <variable_assignment> must be in the form <name>=<value>, where <name> will be checked if it is a valid shell name, otherwise an error status code is returned.

In application code where the inputs are trusted, you may opt for the var macro instead, which generates faster code, but does not validate inputs.

Warning

Note that even with checks to verify that <name> is a valid name for a shell variable, ensuring this function will not accidentally execute its parameters as code instead of making a variable assignment, using untrusted inputs is still not completely safe. Any variable can be assigned with this function, which may affect the program's (and even the shell's) internal state in unpredictable ways.

Options

Operands
<variable_assignment>: A variable assignment in the format <name>=<value>.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).
  • 26: Invalid variable name.
  • 123: Unexpected error (abort).

Abort

  • Aborts on invalid or missing arguments.
  • Aborts on unexpected error.

Usage examples

# Assign the result of a function to the <out_var> parameter
assign_variable "${1}=${result}" || abort "Invalid variable name '${1}'"

nice_things/lang/is_name.sh

is_name

Since 0.3.0 · Source

import "{ is_name }" from nice_things/lang/is_name.sh

Synopsis
is_name <string>…

Configuration

Description
Check if <string> is a valid name that can be used to name a shell variable or function. Return 0 if all arguments are names, 1 if any argument is not a valid name.

Options

Operands
<string>: Name to check.

Stdin

Stdout

Stderr

Exit status

  • 0: All arguments are names.
  • 1: One or more arguments are not valid names.

Abort

Usage examples

is_name "$1" || abort "Invalid parameter: <out_var> must be a valid name"

nice_things/lang/quote.sh

quote

Since 0.3.0 · Source

import "{ quote }" from nice_things/lang/quote.sh

Synopsis
quote <out_var> [<string>…]

Configuration

Description
Quote <string> arguments to make them safe to eval; assign to <out_var> as a quoted list separated by a space character.

For streaming use-cases, when you want to read text from stdin and print quoted text to stdout, in a pipeline, check out the escape_single_quotes function.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <string>: A value to be quoted.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

# Save quoted arguments in a variable
quote NS__arguments "$@"

# Restore arguments from quoted variable
eval " set -- ${NS__arguments}"

nice_things/log/abort.sh

abort

Since 0.3.0 · Source

import "{ abort }" from nice_things/log/abort.sh

Synopsis
abort <message> [<status_code>]

Configuration

Description
Print a message to log_error and exit the current process with an error status code.

The optional parameter <status_code> can be used to specify the exit status. If omitted, the status of the last command will be used if it failed, otherwise the program will exit will status code 1.

Options

Operands

  • <message>: A message to print to stderr.
  • <status_code>: An optional status code to return for the current process.

Stdin

Stdout

Stderr
The <message> will be printed to log_error.

Exit status
From parameter <status_code> if provided, else the exit status of the last command if an error, else 1.

Abort
Always exits the process.

Usage examples

: >/path/to/file || abort "Failed to create file at /path/to/file"

nice_things/log/log.sh

This module provides log functions. There are six levels of logs, from lowest to highest: 0 | none, 1 | error, 2 | warn, 3 | info, 4 | debug, 5 | trace. When a log level is chosen, all lower levels up to that level are active. Level 3 | info is the default.

The run time log level is configured using an environment variable, which defaults to LOG_LEVEL, but can be changed in nice_package.conf. A log level can be chosen either by name or by number. Besides the log level, color or no-color can be set, after a comma. The output is colored by default, and that can also be changed in nice_package.conf.

Because the configuration applies to the whole module, it is documented here instead of in each function bellow:

[module:nice_things/log/log.sh]
# If true, info logs will be preceded by their level name (INFO), like the other levels
named_info=false
# If true, runtime config will default to 'no-color'
no_color=false
# Name of the variable used for runtime configuration
variable=LOG_LEVEL

initialize_log

Since 0.3.0 · Source

import "{ initialize_log }" from nice_things/log/log.sh

Synopsis
initialize_log [<level>] [<color>]

Configuration

Description
Initialize log functions. Logs are initialized on import, you don't need to call this function unless you want to change logging configuration, e.g., if you accept "quiet" or "verbose" command-line options.

Logs can be initialized many times, with optional changes of <level> and/or <color> settings. If a parameter is null or omitted, the value of the LOG_LEVEL environment variable is used if set, otherwise defaults from nice_package.conf are used.

Options

Operands

  • <level>: A log level as number between 0-5, or by name. Optional, or can be set to the null string to ignore.
  • <color>: Optional, may be set to color or no-color.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

initialize_log info color

log_debug

Since 0.3.0 · Source

import "{ log_debug }" from nice_things/log/log.sh

Synopsis
log_debug <message>…

Configuration

Description
Print debug log to stderr.

Options

Operands
<message>: Log message.

Stdin

Stdout

Stderr
Log message output.

Exit status
0: Successful completion.

Abort

Usage examples

log_debug "Got arguments: $(to_string "$@")"

log_error

Since 0.3.0 · Source

import "{ log_error }" from nice_things/log/log.sh

Synopsis
log_error <message>…

Configuration

Description
Print error log to stderr.

Options

Operands
<message>: Log message.

Stdin

Stdout

Stderr
Log message output.

Exit status
0: Successful completion.

Abort

Usage examples

log_error "Something went wrong"

log_info

Since 0.3.0 · Source

import "{ log_info }" from nice_things/log/log.sh

Synopsis
log_info <message>…

Configuration

Description
Print info log to stderr.

Options

Operands
<message>: Log message.

Stdin

Stdout

Stderr
Log message output.

Exit status
0: Successful completion.

Abort

Usage examples

log_info "Some information about the process"

log_is_level

Since 0.3.0 · Source

import "{ log_is_level }" from nice_things/log/log.sh

Synopsis
log_is_level <level>

Configuration

Description
Check if logs up to the specified <level> are active. <level> can be either the name or number of a log level. Accepted levels are 1 | error, 2 | warn, 3 | info, 4 | debug, 5 | trace.

Options

Operands
<level>: A log level.

Stdin

Stdout

Stderr

Exit status

  • 0: Log <level> is active.
  • 1: Log <level> is not active.

Abort

Usage examples

# log_is_level can be use to avoid expanding expensive expressions when a log won't be printed
if log_is_level trace; then
	log_trace "Got arguments: $(to_string "$@")"
fi

log_trace

Since 0.3.0 · Source

import "{ log_trace }" from nice_things/log/log.sh

Synopsis
log_trace <message>…

Configuration

Description
Print trace log to stderr.

Options

Operands
<message>: Log message.

Stdin

Stdout

Stderr
Log message output.

Exit status
0: Successful completion.

Abort

Usage examples

log_trace "Got arguments: $(to_string "$@")"

log_warn

Since 0.3.0 · Source

import "{ log_warn }" from nice_things/log/log.sh

Synopsis
log_warn <message>…

Configuration

Description
Print warning log to stderr.

Options

Operands
<message>: Log message.

Stdin

Stdout

Stderr
Log message output.

Exit status
0: Successful completion.

Abort

Usage examples

log_warn "Something non-critical went wrong"

nice_things/storage/create_runtime_file.sh

create_runtime_file

Since 0.3.0 · Source

import "{ create_runtime_file }" from nice_things/storage/create_runtime_file.sh

Synopsis
create_runtime_file [-s] [-d | -f | -t] [--] <file>

Configuration

Description
Create a runtime file under process_runtime_dir, or optionally under program_runtime_dir when -s is used.

The <file> operand can optionally include preceding directories. The directories are always created, while the file is not created by default, unless option -f or -t is used.

If <file> contains a path, it must be relative and not start with /, ./ or ../, just the name of the directories leading to the file.

By default, this function creates a path to a file that is exclusive for the current process. This directory is deleted on program exit. The option -s can be used to create a path that is shared by all instances of the current program. Note that, when using -s, multiple processes can interfere with each other.

Options

  • -d: Create a directory instead of a file.
  • -f: Force overwrite an existing file.
  • -s: Use a directory that is shared by all instances of the program.
  • -t: Create the file if it does not exist.

Operands
<file>: Name of the file, with optional relative path.

Stdin

Stdout
Prints the absolute path to the file.

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).
  • 14: Base directory missing.
  • 15: Failed to create directory.
  • 16: Failed to write file (when option -f is used).
  • 17: File exists (when option -t is used).

Abort
Aborts on invalid or missing arguments.

Usage examples

# Touch a temporary file
temp_file=$(create_runtime_file -t temp_file.txt)

nice_things/storage/destructor_add.sh

destructor_add

Since 0.3.0 · Source

import "{ destructor_add }" from nice_things/storage/destructor_add.sh

Synopsis
destructor_add <command> [<arg>…]

Configuration

Description
Register a destructor command/function to be executed on program exit. This function can be invoked many times to register many destructors. Once added, destructors cannot be removed, so destructor functions should ideally be idempotent.

This function registers a trap, taking care to make sure it is executed on any shell no matter if the process exits normally or is terminated by a signal.

Note

When destructors are in use, traps for EXIT and the usual termination signals HUP INT QUIT ABRT ALRM TERM should not be configured manually, or they will override the destructors configured through this function.

Options

Operands

  • <command>: The destructor command.
  • <arg>: Optional argument to <command>.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 11: Invalid or missing arguments (abort).

Abort
Aborts on invalid or missing arguments.

Usage examples

# Delete a temporary file on exit
destructor_add rm -f /path/to/temp/file

nice_things/storage/process_runtime_dir.sh

readonly:process_runtime_dir

Since 0.3.0 · Source

import "{ readonly:process_runtime_dir }" from nice_things/storage/process_runtime_dir.sh

Synopsis
"${process_runtime_dir}"

Configuration

Description
The runtime directory for the current process. Will use the value of the program_runtime_dir variable, appending /nice_things/process_runtime_dir/$$, with the PID of the current process as last pathname component. The directory is created on initialization.

A cleanup function is registered with destructor_add on initialization to delete this directory when the program exits.

Normally, this variable should not be used directly by user code. To create runtime files, use the create_runtime_file function instead.

Options

Operands

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful definition.
  • 13: Failed to create directory (on initialization) (abort).

Abort
Aborts if initialization fails.

Usage examples

# Print all runtime files created by the process
ls -A "$process_runtime_dir"

nice_things/storage/program_runtime_dir.sh

readonly:program_runtime_dir

Since 0.3.0 · Source

import "{ readonly:program_runtime_dir }" from nice_things/storage/program_runtime_dir.sh

Synopsis
"${program_runtime_dir}"

Configuration

name=<program_name>

Description
The runtime directory for the current program. Will use the value of the user_runtime_dir variable, appending the package name as last pathname component. The directory is created on initialization.

Normally, this variable should not be used directly by user code. To create runtime files, use the create_runtime_file function instead.

Options

Operands

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful definition.
  • 13: Failed to create directory (on initialization) (abort).

Abort
Aborts if initialization fails.

Usage examples

# Print all runtime files created by the program
ls -A "$program_runtime_dir"

nice_things/storage/user_runtime_dir.sh

readonly:user_runtime_dir

Since 0.3.0 · Source

import "{ readonly:user_runtime_dir }" from nice_things/storage/user_runtime_dir.sh

Synopsis
"${user_runtime_dir}"

Configuration

Description
The runtime directory for the current user. Will use the value of the XDG_RUNTIME_DIR environment variable if it is set and the directory exists, or fall back to the directory at /run/user/${UID} if it exists, or fall back to /tmp/user/${UID}, in which case the directory will be created on initialization.

When the XDG_RUNTIME_DIR environment variable is set to an existing directory, there are no external dependencies. In the cases when knowing the User ID is necessary, the initialization of this variable depends on the id external utility.

This variable may be used to read files or named pipes published by other programs in this directory. To create your own runtime files, use the create_runtime_file function instead.

Options

Operands

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful definition.
  • 13: Failed to create directory (on initialization) (abort).

Abort
Aborts if initialization fails.

Usage examples

# Read a file/named pipe from user runtime directory
do_something <"${user_runtime_dir}/path/to/file"

nice_things/text/escape_single_quotes.sh

escape_single_quotes

Since 0.3.0 · Source

import "{ escape_single_quotes }" from nice_things/text/escape_single_quotes.sh

Synopsis
escape_single_quotes [<text>]

Configuration

Description
Escape <text> for use in a shell script single-quoted string, and print the result. If the <text> parameter is omitted, text will be read from stdin.

Internally, this function uses the substitute_characters function. All occurrences of ' are substituted with '"'"'.

This function exists mostly for streaming use-cases. If you want to quote some text to make it safe to eval, consider using the quote function instead.

Options

Operands
<text>: The text to operate on. If omitted, stdin will be used.

Stdin
The text will be read from stdin if the <text> parameter is omitted.

Stdout
The result will be printed to stdout.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

escape_single_quotes </path/to/file

nice_things/text/substitute_characters.sh

substitute_characters

Since 0.3.0 · Source

import "{ substitute_characters }" from nice_things/text/substitute_characters.sh

Synopsis
substitute_characters <pattern> <replacement> [<text>]

Configuration

Description
Substitute every instance of the <pattern> characters in <text> with <replacement> string, and print the result. If the <text> parameter is omitted, text will be read from stdin.

This function uses only shell builtins and has no external dependencies (e.g. on sed). This is slower than using sed on large inputs, but can be faster on many invocations with small inputs, since it avoids forking a new process.

Options

Operands

  • <pattern>: A string with one or more characters to be searched.
  • <replacement>: A string to replace any matched characters.
  • <text>: The text to operate on. If omitted, stdin will be used.

Stdin
The text will be read from stdin if the <text> parameter is omitted.

Stdout
The result will be printed to stdout.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

lf='
'
# Escape all backslash characters '\\', read from stdin
assign escaped_text substitute_characters '\' '\\' </path/to/file

# Encode all line-feed characters as '\n'
assign escaped_text substitute_characters "$lf" '\n' "$escaped_text"

nice_things/text/substitute_expression.sh

substitute_expression

Since 0.3.0 · Source

import "{ substitute_expression }" from nice_things/text/substitute_expression.sh

Synopsis
substitute_expression <expression> <replacement> [<text>]

Configuration

Description
Substitute every match of the <expression> in <text> with the <replacement> string, and print the result. If the <text> parameter is omitted, text will be read from stdin.

This function uses only shell builtins and has no external dependencies (e.g. on sed). This is slower than using sed on large inputs, but can be faster on many invocations with small inputs, since it avoids forking a new process.

Warning

Escaping special characters by prepending a backslash character does not work on mksh. For a portable way to match them literally, use square-bracket character classes instead—e.g. [[] and [\\] instead of \[ and \\.

Note

Matching expressions as done in this function is slow. If you want to substitute single characters, consider using the faster substitute_characters function instead.

Options

Operands

  • <expression>: A shell expression, as used in a case statement.
  • <replacement>: A string to replace any matched text.
  • <text>: The text to operate on. If omitted, stdin will be used.

Stdin
The text will be read from stdin if the <text> parameter is omitted.

Stdout
The result will be printed to stdout.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

lf='
'
# Decode escaped '\\' to a single backslash character, read from stdin
assign decoded_text substitute_expression '[\\][\\]' '\' </path/to/file

# Decode escaped '\n' to line-feed characters
assign decoded_text substitute_expression '[\\]n' "$lf" "$decoded_text"

nice_things/type/class.sh

Utility functions for implementing classes. This module is automatically imported by the class macro for use in the default private methods.

check_reference

Since 0.3.0 · Source

import "{ check_reference }" from nice_things/type/class.sh

Synopsis
check_reference <ref> [<caller_name>]

Configuration

Description
Check if <ref> looks like a reference to an object; abort on failure.

An optional second argument <caller_name> can be used to pass in the name of the calling function, to improve the error message.

Note

This function is used internally by all the default private methods of a class. Calling it directly is rarely necessary because most public methods will start by calling their private getter <Class>_get_, in which case the reference has already been checked.

Options

Operands

  • <ref>: The value to check.
  • <caller_name>: Optional name of the calling function.

Stdin

Stdout

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 12: Invalid reference (abort).

Abort
Aborts on invalid reference.

Usage examples

NS__MyClass_method() {
	check_reference "$1" MyClass_method
	# Do something with "$1"
}

get_instance_id

Since 0.3.0 · Source

import "{ get_instance_id }" from nice_things/type/class.sh

Synopsis
get_instance_id <ref> <out_var>

Configuration

Description
Get the instance id part of an object reference and assign it to a variable. The instance id is a unique identifier of the object instance and can be used as a portion of a variable name, as long as not the beginning of the name. This can be used to create object state.

Note

This is a low-level function used internally by the default private methods of a class. User code will rarely need this.

Options

Operands

  • <ref>: An object reference.
  • <out_var>: Output variable; the result will be written to this variable.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

NS__MyClass_method() {
	check_reference "$1" MyClass_method
	get_instance_id "$1" NS__instance_id
	# Do something with "$NS__instance_id"
}

nice_things/type/create_object.sh

create_object

Since 0.3.0 · Source

import "{ create_object }" from nice_things/type/create_object.sh

Synopsis
create_object <out_var> <class_name> <constructor> [<arg>…]

Configuration

Description
The create_object function can be used to dynamically invoke the constructor of a class at run-time without using the new macro.

The recommended way to create objects is using the new macro which does validation at build-time. The create_object function exists only for those cases when that is not possible, when code is added at run-time, which could not be part of the build process. One example of such a case is when creating objects is needed in macros, since macros are scripts that are added to the build system at run-time, but they are not built themselves.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <class_name>: Name of the class, without namespace prefix.
  • <constructor>: A public constructor function.
  • <arg>: Arguments for the constructor function, if it takes any.

Stdin

Stdout

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

create_object arguments Array Array "$@"

nice_things/macros/class.macro.sh

These macros allow you to declare classes and create objects. Objects provide long-lived state and accompanying behavior that enable the creation of complex types.

Note

Classes in this framework are not meant to introduce an object-oriented programming paradigm. Instead, they were born from a simple need to have local state that can survive recursion, which is not easily achievable in POSIX sh due to the lack of local variables. In the end, this object system provides a little more than just that.

class

Since 0.3.0 · Source

Synopsis
#{{{ class <Name> "{ <field>… }" }}}

Configuration

Description
Declare a class, giving it a name and fields.

Class names MUST be written in UpperCamelCase form. In public modules, it should have the leading namespace prefix NS__ as usual.

Four private methods are created automatically: <Name>_constructor_, <Name>_destructor_, <Name>_get_, <Name>_set_.

  • <Name>_constructor_ <&self> [<field> <value>]…: Create an object and initialize all fields. Optional field-value pairs can be passed as arguments to initialize fields to specific values. Fields not provided in this form will be initialized to null.
  • <Name>_destructor_ <&self>: Destroy the object and clear all fields.
  • <Name>_get_ <&self> [<out_var> <field>]…: Read values from fields into variables.
  • <Name>_set_ <&self> [<field> <value>]…: Assign values to fields.

Options

Operands

  • <Name>: Name of the class. MUST be written in UpperCamelCase form. Can contain namespace prefix.
  • <field>: A field of the objects of this class.

Stdin

Stdout
Renders the fields and methods of the class, and imports the module at nice_things/type/class.sh for the necessary support functions.

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 1: Bad declaration (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
class NS__Array "{
	length,
	slot_l,
	slot_r
}"
#}}}

class_shift

Since 0.3.0 · Source

Synopsis
#{{{ class_shift [<amount>] }}}

Configuration

Description
Shift positional parameters skipping the first.

Works just like the shift shell special builtin utility, but skips shifting parameter $1. For use in class methods, since the first parameter is the self reference <&self>, and keeping it in a positional parameter instead of assigning to a variable is required sometimes, because variables are global, and positional parameters is the only local state available in POSIX sh.

Warning

This macro is only safe to use when the first parameter is an object reference. No runtime quoting or escaping of the value in $1 is attempted for performance reasons.

Options

Operands
<amount>: Number of parameters to shift. Defaults to 1 if not provided. Can be the name of a runtime variable containing the number.

Stdin

Stdout
One line of code is printed.

Stderr

Exit status
0: Successful completion.

Abort

Usage examples

# Shift positional parameters by 1
#{{{ class_shift }}}

# Shift using dynamic runtime value
NS__shifts=2
#{{{ class_shift NS__shifts }}}

new

Since 0.3.0 · Source

Synopsis
<out_var>=#{{{ new <constructor> }}} [<arg>…]

Configuration

Description
Create a new object of a class. Must always be assigned to a variable. Arguments to the constructor must come after the macro, outside the macro context.

Options

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <constructor>: A public constructor function.
  • <arg>: Arguments for the constructor function, if it takes any.

Stdin

Stdout
One line of code is printed, which creates a unique reference and assigns it to <out_var>, and invokes the constructor function.

Stderr
log_error.

Exit status

  • 0: Successful completion.
  • 1: Bad declaration (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
import "{ Array }" from nice_things/collections/Array.sh
#}}}
letters=#{{{ new Array }}} a b c d e

nice_things/macros/nice_build.macro.sh

These macros are not exactly part of the framework's public API. They are embedded directly in nice_build.sh, the template renderer used by the nice_things framework. Since these names are global, they are accessible in macro context at any time during the build process.

The functions documented in this page are not the only ones available during build. Almost all functions in the framework are imported globally in nice_build and can be invoked in macro context. The main exception are classes which are omitted, except for a few methods being used by the build system, like Array, Array_get, Array_length, Array_push; Map, Map_get, Map_set; PipeStatus, catch, try.

import

Since 0.3.0 · Source

Synopsis
import ["{ <name> [as <alias>], … }" from] <module>

Configuration

name=<package_name>
namespace=<package_namespace>

Description
Import functions and read-only variables from modules. Several modules can be imported in a single invocation, each with optional aliases for the names being imported.

Names being aliased by this function must have been published by the imported module using the public macro. Importing specific names can be omitted, in which case a module is imported just for its global names or side-effects.

Imported modules and each specific alias are rendered only once, on the first time they appear. Which means this macro may not print anything at the point of use if <module> has already been imported before as a dependency of another module.

The import function performs automatic namespace substitution on the imported modules. The namespace configuration property indicates the per-package namespace, and is optional. If omitted, it will fall back to using the package name as namespace. The name configuration property is itself also optional, falling back to using the directory name as package name.

Options

Operands

  • <name>: A public name to be aliased.
  • <alias>: An optional different alias to give <name> in this module's context.
  • <module>: A module specifier.

Stdin

Stdout
The imported modules are printed, each followed by its aliases if specified.

Stderr

Exit status

  • 0: Successful completion.
  • >0: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
# Import a module only for it's side-effects without aliasing any names
import ./src/module.sh

# Import functions from a module to global scope
import "{ function1, function2 }" from ./src/module.sh

# Import readonly variable from a module to global scope
import "{ readonly:var_name }" from ./src/module.sh

# Import functions/readonly variables giving them namespaced aliases in the current module's scope
import "{
		readonly:var_name as NS__var_name,
		function1 as NS__function1,
		function2 as NS__function2
	}" from ./src/module.sh

# Import logging functions from nice_things to global scope
import \
	"{ abort }" from nice_things/log/abort.sh \
	"{ log_error, log_warn, log_info, log_debug, log_trace, log_is_level }" from nice_things/log/log.sh
#}}}

include

Since 0.3.0 · Source

Synopsis
include <module>…

Configuration

Description
Render a module once. Do nothing when trying to render the same <module> again.

Unlike the import macro, this function does not perform namespace substitution, and cannot alias names. It is most similar to the generic render macro, with the main difference being that it will not render the same <module> twice.

When importing source-code modules, you will almost always want to use the import macro instead. include and render are more appropriate for rendering other types of templates, usually with data instead of code, or code in another language instead of shell script.

Options

Operands
<module>: A module specifier.

Stdin

Stdout
The imported modules are printed.

Stderr

Exit status

  • 0: Successful completion.
  • >0: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
# Render a template if not included yet
include ./src/file.template
#}}}

package_conf_get_bool

Since 0.3.0 · Source

Synopsis
package_conf_get_bool [-o | -r] <section> <property>

Configuration

Description
Read a boolean property from nice_package.conf.

The value of boolean properties must be true or false, otherwise the build is aborted with an error.

For a full explanation of all options and arguments, check the description of the package_conf_get_string macro.

Options

  • -o: Let the program configuration (./nice_package.conf), if present, override the configuration from the current package.
  • -r: Read the program configuration (./nice_package.conf) instead of the configuration for the current package. Note that this mode will abort if the configuration file does not exist.

Operands

  • <section>: Name of the section, including the square brackets.
  • <property>: Name of the property.

Stdin

Stdout

Stderr

Exit status

  • 0: Property is true.
  • 1: Property is false.
  • >1: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

NS__no_color=#{{{
# If config property is set to true, set this variable
if package_conf_get_bool module no_color; then
	printf 1
fi
#}}}

package_conf_get_string

Since 0.3.0 · Source

Synopsis
package_conf_get_string [-o | -r] <out_var> <section> <property>

Configuration

Description
Read a string property from nice_package.conf.

By default this macro reads the configuration file for the package the current module is part of. That is, the configuration for the module where it is invoked. The option flags -o and -r can be used to change that behavior, letting the configuration for the program being built override the module configuration, or reading only the program configuration, respectively.

Besides the name of a section in the configuration file, the <section> argument also accepts two special literal values:

  • module: A dynamic section named after the module identifier of the form [module:path/to/module.sh].
  • package: A dynamic section named after the package of the form [package:package_name].

When using the dynamic sections, the options -o and -r are ignored, they always behave as if the -o option is set.

The dynamic section format is the recommended way to configure public modules in library packages. They allow configuration either at the package or at the module level and protect against name collisions. Here is an example configuration from the nice_things framework:

[package:nice_things]
# Value to be assigned to the IFS variable in strict_mode; accepts printf-style escape sequences
strict_mode_ifs=\037

[module:nice_things/log/log.sh]
# If true, info logs will be preceded by their level name (INFO), like the other levels
named_info=false
# If true, runtime config will default to 'no-color'
no_color=false
# Name of the variable used for runtime configuration
variable=LOG_LEVEL

In framework code those can be read as:

#{{{
# Read package configuration
# Note how `package` must be explicitly set for this to work in public modules
package=nice_things package_conf_get_string out_var package strict_mode_ifs
#}}}

#{{{
# Read configuration specific to the current module
package_conf_get_string out_var module variable
#}}}

Options

  • -o: Let the program configuration (./nice_package.conf), if present, override the configuration from the current package.
  • -r: Read the program configuration (./nice_package.conf) instead of the configuration for the current package.

Operands

  • <out_var>: Output variable; the result will be written to this variable.
  • <section>: Name of the section, including the square brackets.
  • <property>: Name of the property.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: <property> not found.
  • >1: Unexpected error (abort).
  • 21: Invalid property value in config file. Expected string but found array.

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

NS__param_level=${1:-"{{{" package_conf_get_string value module variable && printf '${%s-}' "$value" "}}}"}

package_conf_print_string

Since 0.3.0 · Source

Synopsis
package_conf_print_string [-o | -r] <section> <property>

Configuration

Description
Print the value of a string property from nice_package.conf.

For a full explanation of all options and arguments, check the description of the package_conf_get_string macro.

Options

  • -o: Let the program configuration (./nice_package.conf), if present, override the configuration from the current package.
  • -r: Read the program configuration (./nice_package.conf) instead of the configuration for the current package.

Operands

  • <section>: Name of the section, including the square brackets.
  • <property>: Name of the property.

Stdin

Stdout
The value of the property is printed.

Stderr

Exit status

  • 0: Successful completion.
  • 1: <property> not found.
  • >1: Unexpected error (abort).
  • 21: Invalid property value in config file. Expected string but found array.

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

# Print the module specifier and package version for the current module
# {{{$module}}} {{{ package_conf_print_string "[]" version }}}

package_get

Since 0.3.0 · Source

Synopsis
package_get <package> [<out_var> <property>]…

Configuration

Description
Read package properties.

The list of properties a package has are:

  • name: Name of the package; the value of the name property in nice_package.conf if specified, or fall back to dir_name (this function retrieves a package by name, so this property is redundant).
  • namespace: The package namespace; the value of the namespace property in nice_package.conf if specified, or fall back to the name of the package.
  • dir_name: The name of the root directory of the package.
  • path: Relative path to the root directory of the package.
  • config_file: Relative path to the package's configuration file nice_package.conf, if it exists; null otherwise.
  • config: A reference to the package's Config object, if it exists; null otherwise.

Note

This function was made for internal use in nice_build. Many other macros exist to read a package's configuration, so look at them first: package_conf_get_bool, package_conf_get_string, package_conf_print_string.
It is unlikely that application code will need to access a package's data directly using this macro.

Options

Operands

  • <package>: Name of package.
  • <out_var>: Output variable; the result will be written to this variable.
  • <property>: Property to retrieve.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • 1: One or more <property> does not exist in the package.
  • 3: <package> not found.

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
package_get "$package" package_path path || exit
log_debug "The package '${package}' is at path '${package_path}'"
#}}}

public

Since 0.3.0 · Source

Synopsis
public [readonly:]<name>…

Configuration

Description
Publish a <name> from the current module.

A <name> can be a namespaced function (NS__function_name) or read-only variable (readonly NS__var_name), by prepending the readonly: prefix to the public declaration. Names must be public to be imported by dependent modules with the import macro.

Warning

The public declaration should always be the first macro at the top of a module. To prevent errors on circular dependencies, the import macro must always come after the public declaration.

Note that the complete names are used in the public declaration including their namespaces, but the modules importing them omit the namespace as that is automatically added by the import macro. Only namespace'd names can be declared public; there is no reason to publish non-namespace'd names as they are already global.

Names ending in an underscore character (_) are private and cannot be published. Use this mechanism to explicitly mark private names to prevent accidentally leaking them on the public interface.

Options

Operands
<name>: A namespaced function or read-only variable in the current module.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • >0: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
public \
	readonly:NS__var_name \
	NS__some_function \
	NS__another_function
#}}}

render

Since 0.3.0 · Source

Synopsis
render [<module>…]

Configuration

Description
Render a template to stdout. Templates are read from <module> arguments. If no arguments are provided, the template is read from stdin.

For details on the template format, refer to the documentation of the template engine at https://codeberg.org/nice_things/template.sh

Note

If you want to print the contents of a file but do not need to render it as a template, you can simply call the cat function in macro context.

Options

Operands
<module>: A module specifier.

Stdin
Only read if no arguments are provided.

Stdout
The rendered template is printed.

Stderr

Exit status

  • 0: Successful completion.
  • >0: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

#{{{
# Render a template in the current position
render ./src/file.template
#}}}

use_macro

Since 0.3.0 · Source

Synopsis
use_macro <macro>…

Configuration

Description
Execute macros in the current context.

Application macros are automatically executed from the file at ./src/main.macro.sh, and macros from the nice_things framework are also executed automatically. If another dependency provides public macros for application use, you will have to execute them manually using this function. Normally you will invoke the use_macro function in the application's ./src/main.macro.sh file to load macros before rendering starts.

Options

Operands
<macro>: A module specifier pointing to a macro file.

Stdin

Stdout

Stderr

Exit status

  • 0: Successful completion.
  • >0: Unexpected error (abort).

Abort
Any failure will abort the build process. No runtime abort is added.

Usage examples

use_macro package/path/to/file.macro.sh

readonly:module

Since 0.3.0 · Source

Synopsis
{{{$module}}}

Configuration

Description
Module identifier of the current module.

Options

Operands

Stdin

Stdout

Stderr

Exit status

Abort

Usage examples

# Module: {{{$module}}}

readonly:module_path

Since 0.3.0 · Source

Synopsis
{{{$module_path}}}

Configuration

Description
Relative path to the current module.

Options

Operands

Stdin

Stdout

Stderr

Exit status

Abort

Usage examples

# Module path: {{{$module_path}}}

readonly:ns

Since 0.3.0 · Source

Synopsis
{{{$ns}}}

Configuration

Description
Namespace of the current module.

Options

Operands

Stdin

Stdout

Stderr

Exit status

Abort

Usage examples

# Module namespace: {{{$ns}}}

readonly:package

Since 0.3.0 · Source

Synopsis
{{{$package}}}

Configuration

Description
Name of the package the current module belongs to.

Options

Operands

Stdin

Stdout

Stderr

Exit status

Abort

Usage examples

# Package: {{{$package}}}

readonly:root_package

Since 0.3.0 · Source

Synopsis
{{{$root_package}}}

Configuration

Description
Name of the root package. This is the program being built.

Options

Operands

Stdin

Stdout

Stderr

Exit status

Abort

Usage examples

# Program: {{{$root_package}}}

nice_things/macros/strict_mode.macro.sh

lenient_mode

Since 0.3.0 · Source

Synopsis
#{{{ lenient_mode }}}

Configuration

Description
Revert the configuration changes made by the strict_mode macro back to defaults.

Options

Operands

Stdin

Stdout
Two lines of code are printed.

Stderr

Exit status

  • 0: Successful completion.
  • 123: Unexpected error (abort).

Abort
Aborts on unexpected error.

Usage examples

#{{{ lenient_mode }}}

strict_mode

Since 0.3.0 · Source

Synopsis
#{{{ strict_mode }}}

Configuration

[package:nice_things]
# Value to be assigned to the IFS variable in strict_mode; accepts printf-style escape sequences
strict_mode_ifs=\037

Description
Change shell options to make behavior more predictable and robust. The line of code printed by this macro does two things, it sets shopts set -Cefu, and it changes the value of IFS to \037 (Unit Separator control code 0x1F). The default value of IFS can be overridden in nice_package.conf.

  • set -C: (noclobber) Prevent existing files from being overwritten by the shell's > redirection operator. Use the explicit overwrite operator >| when overwriting is desired.
  • set -e: (errexit) Exit immediately if any untested command fails.
  • set -f: (noglob) Disable pathname expansion. Use glob to perform globbing.
  • set -u: (nounset) Exit immediately when attempting to expand a variable that is not set.

This configuration can be reverted to defaults using the accompanying lenient_mode macro.

Note

You may have noticed the omission of a pipefail option in strict_mode. That is because pipefail is a bash feature and is not available in other POSIX shells. Still, checking the return status of every stage of a pipeline for errors is very important for program robustness. For that reason the framework offers an equivalent alternative in the PipeStatus class.

Options

Operands

Stdin

Stdout
One line of code is printed.

Stderr

Exit status

  • 0: Successful completion.
  • 123: Unexpected error (abort).

Abort
Aborts on unexpected error.

Usage examples

#!/bin/sh
# shellcheck disable=SC2046,SC2086
#{{{ strict_mode }}}

nice_things/macros/var.macro.sh

var

Since 0.3.0 · Source

Synopsis
#{{{ var <assignee>=<assignor> [<assignee>=<assignor>]… }}}

Configuration

Description
Use indirection to dynamically assign variables.

No validation checks are done at run-time, which makes the code printed by this macro much faster than invoking the assign_variable function, but unsafe to use on untrusted inputs.

Three forms of <assignee> can be used:

  • mandatory: The default; simply use the name of a variable or positional parameter as <assignee>. Will abort at run-time if <assignee> is null.
  • optional: Append a question mark ? to the <assignee> name: <assignee>?=<assignor>; Will do nothing if <assignee> is null at run-time.
  • explicit: Write your own parameter expansion in the <assignee> position, implementing whatever rules you want. For advanced use-cases, like dynamic buckets in a data structure.

Options

Operands

  • <assignee>: Variable or positional parameter containing the name of the variable to be assigned.
  • <assignor>: Variable or positional parameter holding the value to assign.

Stdin

Stdout
One line of code is printed.

Stderr

Exit status
0: Successful completion.

Abort
Aborts if <assignee> is null.

Usage examples

# Assign the result of a function to the <out_var> parameter
#{{{
var 1=result
#}}}

# Assign the result of a function to an optional <out_var> parameter
#{{{
var '1?=result'
#}}}

# Example taken from Array_bucket_set_
# Assign values to arbitrary variable name expansions
#{{{
# shellcheck disable=SC2016
var 'NS__bucket_${1:?}__${2:?}=${3}' \
	'NS__bucket_${1}__${2}_len=${4}'
#}}}